package one.stand.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import one.stand.code.RedisCode;
import one.stand.config.WxConfig;
import one.stand.config.WxUrlConfig;
import one.stand.entity.OrderNoEntity;
import one.stand.enums.LoginEnum;
import one.stand.exception.GoPayException;
import one.stand.model.CompanyAccountModel;
import one.stand.model.ResultModel;
import one.stand.request.CompanyAccountRequest;
import one.stand.service.CompanyAccontService;
import one.stand.service.WxPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: QIK
 * @CreateDate: 2019/4/29 11:32
 */
@Service
public class WxPayServiceImpl extends PublicBaseService implements WxPayService {

    /**
     * 成功码
     */
    public final static String SUCCESS = "SUCCESS";

    /**
     * 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"
     */
    private final static String deviceInfo = "";

    /**
     * 符合ISO 4217标准的三位字母代码，默认人民币：CNY
     */
    private final static String fee_type = "CNY";

    /**
     * JSAPI：JSAPI支付 NATIVE：Native支付 APP：APP支付
     */
    private final static String trade_type = "JSAPI";

    /**
     * 微信提现请求Url
     */
    private final static String transfers = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

    @Override
    public Map<String, String> payCallback(String notifyData) throws Exception {
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
        String outTradeNo = notifyMap.get("out_trade_no");
        OrderNoEntity entity = redisUtil.get(RedisCode.getUserOrderKey(outTradeNo));
        if(entity != null) {
            WxConfig wxConfig = wxConfig(entity.getOrdersId());
            WXPay wxpay = new WXPay(wxConfig);
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                return notifyMap;
            }
        }
        return null;
    }

    @Override
    public Map<String, String> wxPay(String body, String orderNum, String totalFee, String openId, String ip, Integer orderId, String companyKey) throws Exception {
        log.info("微信支付请求参数：{},{},{},{},{},{},{}", body, orderNum, totalFee, openId, ip, orderId, companyKey);
        WxConfig wxConfig = wxConfig(companyKey);
        WXPay wxpay = new WXPay(wxConfig);
        Map<String, String> data = new HashMap<>(12);
        data.put("appid", wxConfig.getAppID());
        data.put("mch_id", wxConfig.getMchID());
        data.put("body", body);
        data.put("out_trade_no", orderNum);
        data.put("device_info", deviceInfo);
        data.put("fee_type", fee_type);
        data.put("total_fee", totalFee);
        data.put("spbill_create_ip", ip);
        data.put("notify_url", wxConfig.getNotifyUrl());
        data.put("trade_type", trade_type);
        data.put("openid", openId);
        String sign = WXPayUtil.generateSignature(data, wxConfig.getKey());
        data.put("sign", sign);
        Map<String, String> resp = wxpay.unifiedOrder(data);
        log.info("微信支付返回：" + JSON.toJSONString(resp));
        if (SUCCESS.equals(resp.get("return_code"))) {
            Map<String, String> reData = new HashMap<>(7);
            reData.put("appId", wxConfig.getAppID());
            reData.put("nonceStr", resp.get("nonce_str"));
            String newPackage = "prepay_id=" + resp.get("prepay_id");
            reData.put("package", newPackage);
            reData.put("signType", "MD5");
            reData.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
            String newSign = WXPayUtil.generateSignature(reData, wxConfig.getKey());
            resp.put("paySign", newSign);
            resp.put("timeStamp", reData.get("timeStamp"));
            return resp;
        } else {
            throw new Exception(resp.get("return_msg"));
        }
    }

    @Override
    public ResultModel refund(String tradeNo, String outRefundNo, String totalFee, String refundFee, String refundDesc, Integer orderId) throws Exception {
        log.info("微信退款请求参数：{},{},{},{},{},{}", tradeNo, outRefundNo, totalFee, refundFee, refundDesc, orderId);
        WxConfig wxConfig = wxConfig(orderId);
        WXPay wxpay = new WXPay(wxConfig);
        Map<String, String> data = new HashMap<>(10);
        data.put("appid", wxConfig.getAppID());
        data.put("mch_id", wxConfig.getMchID());
        data.put("nonce_str", getRandomNumber());
        data.put("out_trade_no", tradeNo);
        data.put("out_refund_no", outRefundNo);
        data.put("total_fee", totalFee);
        data.put("refund_fee", refundFee);
        data.put("refund_desc", refundDesc);
        /** data.put("notify_url", wxConfig.getRefundUrl());*/
        String sign = WXPayUtil.generateSignature(data, wxConfig.getKey());
        data.put("sign", sign);
        Map<String, String> resp = wxpay.refund(data);
        log.info("微信退款返回：" + JSON.toJSONString(resp));
        if (SUCCESS.equals(resp.get("return_code"))) {
            if (SUCCESS.equals(resp.get("result_code"))) {
                return ResultModel.success(resp);
            } else {
                return ResultModel.fail(resp.get("err_code_des"));
            }
        } else {
            throw new Exception(resp.get("return_msg"));
        }
    }

    @Override
    public ResultModel doCash(String recordNo, String openid, Integer amount, String ip, String companyKey) {
        log.info("微信提现接口参数：{}，{}，{}",openid, amount, companyKey);
        if(StringUtils.isEmpty(openid) || StringUtils.isEmpty(companyKey) || amount == null){
            return ResultModel.checkParamFail();
        }
        try {
            WxConfig wxConfig = wxConfig(companyKey);
            WXPay wxpay = new WXPay(wxConfig);
            Map<String, String> data = new HashMap<>(10);
            data.put("mch_appid", wxConfig.getAppID());
            data.put("mchid", wxConfig.getMchID());
            data.put("nonce_str", getRandomNumber());
            data.put("partner_trade_no", recordNo);
            data.put("openid", openid);
            data.put("check_name", "NO_CHECK");
            data.put("amount", String.valueOf(amount));
            data.put("desc", "微信提现");
            data.put("spbill_create_ip", ip);
            String sign = WXPayUtil.generateSignature(data, wxConfig.getKey());
            data.put("sign", sign);
            String str = wxpay.requestWithCert(transfers, data, 60000, 60000);
            log.info("微信提現返回：{}" , str);
            Map<String, String> resp = WXPayUtil.xmlToMap(str);
            if (SUCCESS.equals(resp.get("return_code"))) {
                if (SUCCESS.equals(resp.get("result_code"))) {
                    return ResultModel.success(resp);
                }
            }
            throw new Exception(resp.get("err_code_des"));
        }catch(Exception e){
            return ResultModel.fail(e.getMessage());
        }
    }



    @Autowired
    WxUrlConfig wxUrlConfig;

    public WxConfig wxConfig(Integer orderId) throws GoPayException {

        CompanyAccountModel companyAccountModel = companyAccontService.selectByOrdersId(new CompanyAccountRequest() {{
            setOrdersId(orderId.longValue());
            setAccountType(LoginEnum.WX_PAY.getAccountType());
        }});
        if (null == companyAccountModel) {
            log.info("订单未获取到公司账户{}", orderId);
            throw new GoPayException(String.valueOf(orderId));
        }
        WxConfig wxConfig=new WxConfig();
        wxConfig.setAppId(companyAccountModel.getAppId());
        wxConfig.setKey(companyAccountModel.getKey());
        wxConfig.setMchId(companyAccountModel.getMerchantId());
        wxConfig.setCertData(Base64.getDecoder().decode(companyAccountModel.getCertData()));
        wxConfig.setNotifyUrl(wxUrlConfig.getNotifyUrl());
        wxConfig.setRefundUrl(wxUrlConfig.getRefundUrl());
        wxConfig.setHttpConnectTimeoutMs(5000);
        wxConfig.setHttpReadTimeoutMs(5000);
        return wxConfig;
    }

    @Autowired
    CompanyAccontService companyAccontService;
    public WxConfig wxConfig(String companyKey) throws GoPayException {
        CompanyAccountModel companyAccountModel = companyAccontService.selectByCompanyKeyDefault(new CompanyAccountRequest() {{
            setCompanyKey(companyKey);
            setAccountType(LoginEnum.WX_PAY.getAccountType());
        }});
        if (null == companyAccountModel) {
            log.info("未获取到公司账户{}", companyKey);
            throw new GoPayException("未获取到公司账户~!");
        }
        if(companyAccountModel.getState() != 1){
            log.info("公司账户认证未成功{}", JSON.toJSONString(companyAccountModel));
            throw new GoPayException("公司账户认证未成功~!");
        }
        WxConfig wxConfig=new WxConfig();
        wxConfig.setAppId(companyAccountModel.getAppId());
        wxConfig.setKey(companyAccountModel.getKey());
        wxConfig.setMchId(companyAccountModel.getMerchantId());
        wxConfig.setCertData(Base64.getDecoder().decode(companyAccountModel.getCertData()));
        wxConfig.setNotifyUrl(wxUrlConfig.getNotifyUrl());
        wxConfig.setRefundUrl(wxUrlConfig.getRefundUrl());
        wxConfig.setHttpConnectTimeoutMs(5000);
        wxConfig.setHttpReadTimeoutMs(5000);
        return wxConfig;
    }


    public static void main(String[] args) {
        String cert="+k98zzJaOqdcn5VwSCvvZRGCqGu8V2/3PPcxSDAhBgkqhkiG9w0BCRQxFB4SAE0ATQBQAGEAeQBDAGUAcgB0MCMGCSqGSIb3DQEJFTEWBBTA4B2Q622fsFAfjUnO4T5lbdow5DAtMCEwCQYFKw4DAhoFAAQUzxalUbDQFXBMvCcVuIJapl4NKsQECChRToC9Owcb";
        System.err.println(JSON.toJSONString(Base64.getDecoder().decode(cert)));
    }
    /**
     * 随机数
     *
     * @return
     */
    private static String getRandomNumber() {
        return System.currentTimeMillis() + "" + (int) (1 + Math.random() * (9999));
    }

    @Override
    public String success() {
        return "<xml> \n" +
                "  <return_code><![CDATA[success]]></return_code>\n" +
                "  <return_msg><![CDATA]></return_msg>\n" +
                "</xml>";
    }

    @Override
    public String fail() {
        return "<xml> \n" +
                "  <return_code><![CDATA[fail]]></return_code>\n" +
                "  <return_msg><![CDATA]></return_msg>\n" +
                "</xml>";
    }
}
